package tld.test.menu.challenges
{
	import flash.display.Sprite;
	import flash.events.AsyncErrorEvent;
	import flash.events.ErrorEvent;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.events.NetStatusEvent;
	import flash.events.SecurityErrorEvent;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	
	import tld.test.interpreter.IContext;
	import tld.test.menu.Challenge;
	import tld.test.menu.Console;
	import tld.test.menu.IChallenge;
	
	public class VideoPlayer extends Sprite implements IChallenge
	{
		private const _challenge:Challenge = new Challenge();
		
		private const _names:Object =
		{
			"play" : play,
			"stop" : stop,
			"reset" : reset
		}
		
		private const _video:Video = new Video();
		
		private const _playButton:TextField = new TextField();
		
		private const _stopButton:TextField = new TextField();
		
		private const _format:TextFormat = 
			new TextFormat("_sans", 12, 0, true);
		
		private var _connection:NetConnection;
		
		private var _stream:NetStream;
		
		private var _url:String;
		
		private var _paused:Boolean;
		
		private var _playOnConnect:Boolean;
		
		private var _console:Console;
		
		public function VideoPlayer()
		{
			super();
			for (var name:String in this._names)
				this._challenge.put(name, this._names[name]);
			this.draw();
		}
		
		public function setConsole(console:Console):void
		{
			this._console = console;
		}
		
		public function challenge():IContext
		{
			return this._challenge;
		}
		
		public function help():String
		{
			return <![CDATA[;;; This is VideoChallenge program

; You are tested on two skills:
;   - play videos
;   - stop videos
; Advanced users can try `reset' option. `reset' requires an argument
; of type String - the URL of the FLV or MP4 video that you want to load.

; call (unload) to unload this challenge.]]>.toString();
		}
		
		public function defaults():String
		{
			return <![CDATA[
			(reset "../assets/20051210-w50s.flv")]]>.toString();
		}
		
		public function reset(url:String):void
		{
			this._url = url;
			this.connect();
		}
		
		public function play():void
		{
			if (this._connection.connected)
			{
				// if we don't play anything for a long time
				// we will get dc'ed.
				this._playOnConnect = false;
				if (this._paused)
					this._stream.resume();
				else 
				{
					this._video.attachNetStream(this._stream);
					this._stream.play(this._url);
				}
			}
			else
			{
				this._playOnConnect = true;
				this.connect();
			}
		}
		
		public function stop():void
		{
			this._paused = true;
			this._stream.pause();
		}
		
		private function draw():void
		{
			super.addChild(this._video);
			this._stopButton.y = this._playButton.y = 240;
			this.drawButton("Play", this._playButton);
			this.drawButton("Stop", this._stopButton);
			this._stopButton.x = this._playButton.width + 1;
			super.addChild(this._stopButton);
			super.addChild(this._playButton);
			this._playButton.addEventListener(
				MouseEvent.CLICK, this.clickHandler);
			this._stopButton.addEventListener(
				MouseEvent.CLICK, this.clickHandler);
		}
		
		private function clickHandler(event:MouseEvent):void
		{
			if (this._console)
			{
				if (event.currentTarget == this._playButton)
					this._console.run("(play)");
				else this._console.run("(stop)");
			}
			else
			{
				if (event.currentTarget == this._playButton)
					this.play();
				else this.stop();
			}
		}
		
		private function drawButton(label:String, button:TextField):void
		{
			button.background = true;
			button.backgroundColor = 0x424242;
			button.selectable = false;
			button.autoSize = TextFieldAutoSize.LEFT;
			button.defaultTextFormat = this._format;
			button.text = label;
		}
		
		private function connect():void
		{
			this._connection = new NetConnection();
			this._connection.connect(null);
			this._connection.addEventListener(
				IOErrorEvent.IO_ERROR, this.errorHandler);
			this._connection.addEventListener(
				SecurityErrorEvent.SECURITY_ERROR, this.errorHandler);
			this._connection.addEventListener(
				NetStatusEvent.NET_STATUS, this.netStatusHandler);
			this._stream = new NetStream(this._connection);
			this._stream.addEventListener(
				NetStatusEvent.NET_STATUS, this.netStatusHandler);
			this._stream.addEventListener(
				SecurityErrorEvent.SECURITY_ERROR, this.errorHandler);
			// We don't care about metadata etc.
			this._stream.addEventListener(
				AsyncErrorEvent.ASYNC_ERROR, this.errorHandler);
		}
		
		private function netStatusHandler(event:NetStatusEvent):void
		{
			switch (event.info.code)
			{
				// There are too many status codes to really 
				// try to handle all of them here. Some other time.
				case "NetConnection.Connect.Success":
					if (this._playOnConnect)
					{
						this._video.attachNetStream(this._stream);
						this._stream.play(this._url);
					}
					break;
				case "NetStream.Play.StreamNotFound":
					throw event.info.code;
					break;
			}
		}
		
		private function errorHandler(event:ErrorEvent):void
		{
			trace(event);
		}
	}
}